home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / human interface toolbox / fragment tool / fragments.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  14.7 KB  |  602 lines

  1. /*
  2.     File:        Fragments.c
  3.  
  4.     Contains:    Code Fragment manipulation routines
  5.  
  6.     Written by: Chris White    
  7.  
  8.     Copyright:    Copyright © 1995-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 8/5/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23.  
  24. #ifndef __MEMORY__
  25.     #include <Memory.h>
  26. #endif
  27.  
  28. #ifndef __RESOURCES__
  29.     #include <Resources.h>
  30. #endif
  31.  
  32. #ifndef __STDDEF__
  33.     #include <stddef.h>
  34. #endif
  35.  
  36. #ifndef __STRING__
  37. //    #include <string.h>
  38. #endif
  39.  
  40.  
  41.  
  42. #ifndef __FRAGMENTTOOL__
  43.     #include "FragmentTool.h"
  44. #endif
  45.  
  46. #ifndef __FRAGMENTSTUFF__
  47.     #include "FragmentStuff.h"
  48. #endif
  49.  
  50. #include "Prototypes.h"
  51.  
  52.  
  53. #include "Utilities.h"
  54.  
  55.  
  56.  
  57. static OSErr SetInternalResourceSize ( Handle theHan, int16 itemCount );
  58. static OSErr DeleteFileData ( FSSpecPtr theSpec, int32 theOffset, int32 theLength );
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65. //
  66. // Translates the 'cfrg' resource into our own data structure. Ours will
  67. // contain the same information in a simpler format, plus a few extra fields.
  68. //
  69. OSErr ParseResource ( Handle theResource, tHeaderHan privateData )
  70. {
  71.     // Copy the relevant items from the 'cfrg' resource into our internal format
  72.     SignedByte    oldResourceState, oldInternalState;
  73.     short        itemCount, index;
  74.     Ptr            itemStart;
  75.     long        headerSize = offsetof(cfrgHeader, arrayStart);
  76.     OSErr        err = noErr;
  77.     
  78.     
  79.     // set the size of the destination block
  80.     itemCount = (*(hdrHand)theResource)->itemCount;
  81.     err = SetInternalResourceSize ( (Handle) privateData, itemCount );
  82.     if ( err )
  83.         goto done;
  84.     
  85.     // Lock the original resource
  86.     oldResourceState = HGetState ( theResource );
  87.     HLock ( theResource );
  88.     
  89.     oldInternalState = HGetState ( (Handle) privateData );
  90.     HLock ( (Handle) privateData );
  91.     
  92.     // Copy the relevant fields from the header
  93.     (*privateData)->version = (*(hdrHand)theResource)->version;
  94.     (*privateData)->itemCount = itemCount;
  95.     
  96.     // Transfer each item from the cfrg into the internal resource
  97.     if (itemCount == 0) goto done;
  98.     itemStart = &(*(hdrHand)theResource)->arrayStart;
  99.     for (index = 0; index < itemCount; index++) {
  100.         cfrgItem*         srcItem;
  101.         tItemPtr         dstItem;
  102.         
  103.         srcItem = (cfrgItem*)itemStart;
  104.         dstItem = &(*privateData)->itemList[index];
  105.         
  106.         // These are just used internally
  107.         dstItem->bDeleted = false;
  108.         dstItem->bExistsInDocument = true;
  109.         dstItem->tempFilePtr = nil;
  110.         
  111.         // These contain the actual fargement data
  112.         dstItem->archType        = srcItem->archType;
  113.         dstItem->updateLevel    = srcItem->updateLevel;
  114.         dstItem->currVersion    = srcItem->currVersion;
  115.         dstItem->oldDefVersion    = srcItem->oldDefVersion;
  116.         dstItem->appStackSize    = srcItem->appStackSize;
  117.         dstItem->appSubFolder    = srcItem->appSubFolder;
  118.         dstItem->usage            = srcItem->usage;
  119.         dstItem->location        = srcItem->location;
  120.         dstItem->codeOffset        = srcItem->codeOffset;
  121.         dstItem->codeLength        = srcItem->codeLength;
  122.         BlockMove(srcItem->name, dstItem->name, srcItem->name[0]+1);
  123.         
  124.         itemStart += srcItem->itemSize;
  125.     }
  126.  
  127. done:
  128.     
  129.     HSetState ( theResource, oldResourceState );
  130.     HSetState ( (Handle) privateData, oldInternalState );
  131.     
  132.     return err;    
  133. }
  134.  
  135.  
  136.  
  137. //
  138. // Build the 'cfrg' resource from our own data structures.
  139. //
  140. OSErr BuildResource ( tHeaderHan privateData, Handle theResource )
  141. {
  142.     // Construct a cfrg resource from our internal template
  143.     SignedByte        oldResourceState, oldInternalState;
  144.     OSErr            err;
  145.     unsigned long    headerSize =  offsetof(cfrgHeader, arrayStart);
  146.     unsigned long    bytesCopied;
  147.     int                itemCount, deletedCount = 0, index;
  148.  
  149.     // Construct the header by setting the destination handle to that
  150.     // size, clearing the memory, and then inserting the version and
  151.     // item count values
  152.     oldResourceState = HGetState(theResource);
  153.     oldInternalState = HGetState((Handle)privateData);
  154.     HLock((Handle)privateData);
  155.     
  156.     SetHandleSize(theResource, headerSize);
  157.     err = MemError ( );
  158.     if ( err ) goto done;
  159.     
  160.     BlockClear ( *theResource, 0, headerSize );
  161.     
  162.     ((cfrgHeader*)(*theResource))->version = (*privateData)->version;
  163.     itemCount = (*privateData)->itemCount;
  164.     ((cfrgHeader*)(*theResource))->itemCount = itemCount;
  165.     
  166.     // Now, copy each item individually
  167.     bytesCopied = headerSize;
  168.     for (index = 0; index < itemCount; index++)
  169.     {
  170.         cfrgItem*        dstPtr;
  171.         tItemPtr        srcPtr;
  172.         long            itemSize;
  173.         unsigned long    newSize;
  174.         
  175.         srcPtr = &(*privateData)->itemList[index];
  176.         if ( srcPtr->bDeleted )
  177.         {
  178.             deletedCount++;
  179.             continue;
  180.         }
  181.         
  182.         // Calculate the size of this entry
  183.         itemSize = offsetof(cfrgItem, name) + srcPtr->name[0] + 1;
  184.         itemSize += itemSize & 0x0003;    // Pad up to the next multiple of 4
  185.         
  186.         // Extend and clear the handle
  187.         newSize = bytesCopied + itemSize;
  188.         SetHandleSize(theResource, newSize);
  189.         err = MemError ( );
  190.         if ( err ) goto done;
  191.         dstPtr = (cfrgItem*)(((unsigned long)*theResource) + bytesCopied);
  192.         BlockClear ( (Ptr)dstPtr, 0, itemSize );
  193.         
  194.         // Transfer the individual fields
  195.         dstPtr->archType        = srcPtr->archType;
  196.         dstPtr->updateLevel        = srcPtr->updateLevel;
  197.         dstPtr->currVersion        = srcPtr->currVersion;
  198.         dstPtr->oldDefVersion    = srcPtr->oldDefVersion;
  199.         dstPtr->appStackSize    = srcPtr->appStackSize;
  200.         dstPtr->appSubFolder    = srcPtr->appSubFolder;
  201.         dstPtr->usage            = srcPtr->usage;
  202.         dstPtr->location        = srcPtr->location;
  203.         dstPtr->codeOffset        = srcPtr->codeOffset;
  204.         dstPtr->codeLength        = srcPtr->codeLength;
  205.         dstPtr->itemSize        = itemSize;
  206.         BlockMove(srcPtr->name, dstPtr->name, srcPtr->name[0] + 1);
  207.  
  208.         bytesCopied = newSize;
  209.     }
  210.     
  211.     ((cfrgHeader*)(*theResource))->itemCount -= deletedCount;
  212.     
  213.     
  214. done:
  215.     HSetState(theResource, oldResourceState);
  216.     HSetState((Handle)privateData, oldInternalState);
  217.     return err;
  218. }
  219.  
  220.  
  221.  
  222. OSErr CopyFragment ( tHeaderHan sourceHeader, FSSpecPtr sourceSpec, int16 sourceIndex,
  223.                         tHeaderHan targetHeader, FSSpecPtr targetSpec )
  224. {
  225.     SignedByte        sourceState;
  226.     SignedByte        targetState;
  227.     int16            targetIndex = 0;
  228.     OSErr            theErr;
  229.     tItemPtr        targetPtr = nil;
  230.     
  231.     
  232.     // No need to use HLockHi. It's slower, and they're not locked for long
  233.     // enough for it to make any difference here.
  234.     sourceState = HGetState ( (Handle) sourceHeader ); HLock ( (Handle) sourceHeader );
  235.     targetState = HGetState ( (Handle) targetHeader ); HLock ( (Handle) targetHeader );
  236.     
  237.     // Resize the handle based on the new item count
  238.     theErr = SetInternalResourceSize ( (Handle) targetHeader, (*targetHeader)->itemCount + 1 );
  239.     if ( theErr ) goto CleanupAndBail;
  240.     
  241.     // We're appending the data, so the target index is itemCount (Index in zero-based)
  242.     targetIndex = (*targetHeader)->itemCount;
  243.     
  244.     targetPtr = &(*targetHeader)->itemList[targetIndex];
  245.     BlockMoveData ( &(*sourceHeader)->itemList[sourceIndex], targetPtr, sizeof ( tItem ) );
  246.     if ( sourceSpec && targetSpec )
  247.     {
  248.         theErr = AppendFileData ( sourceSpec, targetSpec, &targetPtr->codeOffset, &targetPtr->codeLength );
  249.         if ( theErr ) goto CleanupAndBail;
  250.         
  251.     }
  252.     
  253.     // Success. Now it's safe to update the itemCount
  254.     (*targetHeader)->itemCount++;
  255.     
  256.     return noErr;
  257.     
  258.     
  259. CleanupAndBail:
  260.     
  261.     // Unlock our handles
  262.     HSetState ( (Handle) sourceHeader, sourceState );
  263.     HSetState ( (Handle) targetHeader, targetState );
  264.     // Release any extra storage we may have grabbed
  265.     SetInternalResourceSize ( (Handle) targetHeader, (*targetHeader)->itemCount );
  266.     
  267.     return theErr;
  268. }
  269.  
  270.  
  271.  
  272. //
  273. // This is called twice to actualy delete a fragment. The first time to update
  274. // the data structures we keep in memory. The second time is when the user saves
  275. // the document, and we actually delete the fragment from the data fork.
  276. //
  277. OSErr DeleteFragment ( tHeaderHan theHeader, FSSpecPtr theSpec, int16 theIndex )
  278. {
  279.     SignedByte    theState;
  280.     OSErr        theErr = noErr;
  281.     tItemPtr     theItem = nil;
  282.     
  283.     
  284.     theState = HGetState ( (Handle) theHeader );
  285.     HLock ( (Handle) theHeader );
  286.     
  287.     theItem = GetNthItem ( theHeader, theIndex );
  288.     if ( theItem == nil )
  289.         return kGenericError;
  290.     
  291.     // If the item isn't already marked as deleted, do so
  292.     // and update the header with the new item count.
  293.     if ( !theItem->bDeleted )
  294.         theItem->bDeleted = true;
  295.     
  296.     // If we have a file spec, we need to actually delete the data now.
  297.     if ( theSpec )
  298.     {
  299.         int i;
  300.         
  301.         theErr = DeleteFileData ( theSpec, theItem->codeOffset, theItem->codeLength );
  302.         
  303.         // We need to update the offsets off all the fragments that follow this one
  304.         for ( i = theIndex + 1; i < (*theHeader)->itemCount; i++ )
  305.         {
  306.             tItemPtr    tmpItem;
  307.             
  308.             tmpItem = GetNthItem ( theHeader, i );
  309.             tmpItem->codeOffset -= theItem->codeLength;
  310.         }
  311.     }
  312.     
  313.     HSetState ( (Handle) theHeader, theState );
  314.     
  315.     return noErr;
  316. }
  317.  
  318.  
  319.  
  320. //
  321. // Gets the record pointer given an index
  322. //
  323. tItemPtr GetNthItem ( tHeaderHan theHeader, int16 theIndex )
  324. {
  325.     tItemPtr theItem = nil;
  326.     
  327.     if ( (*theHeader)->itemCount > theIndex )
  328.         theItem = &(*theHeader)->itemList[theIndex];
  329.         
  330.     #if DEBUGGING
  331.     if ( theItem == nil )    DebugStr ( "\p GetNthItem returning nil" );
  332.     #endif
  333.     
  334.     return theItem;
  335. }
  336.  
  337.  
  338.  
  339. //
  340. // Gets the last item. Used for when an item has just been added.
  341. //
  342. tItemPtr GetLastItem ( tHeaderHan theHeader )
  343. {
  344.     int16        theIndex;
  345.     tItemPtr     theItem = nil;
  346.     
  347.     theIndex = (*theHeader)->itemCount - 1;
  348.     theItem = &(*theHeader)->itemList[theIndex];
  349.     
  350.     #if DEBUGGING
  351.     if ( theItem == nil )    DebugStr ( "\p GetLastItem returning nil" );
  352.     #endif
  353.     
  354.     return theItem;
  355. }
  356.  
  357.  
  358.  
  359. //
  360. // Returns the number of fragments currently in the document
  361. //
  362. int16 GetItemCount ( tHeaderHan theHeader )
  363. {
  364.     return (*theHeader)->itemCount;
  365. }
  366.  
  367.  
  368.  
  369. //
  370. // Sets the handle size based on the number of items we pass in
  371. //
  372. static OSErr SetInternalResourceSize ( Handle theHan, int16 itemCount )
  373. {
  374.     SignedByte    theState;
  375.     OSErr        theErr;
  376.     
  377.     // We need to pass SetHandleSize an unlocked handle, but we'll
  378.     // preserve its present state.
  379.     theState = HGetState ( theHan );
  380.     HUnlock ( theHan );
  381.     
  382.     // set the size of the resource based in the item count
  383.     SetHandleSize ( theHan, offsetof ( tHeader, itemList ) +
  384.                                 (itemCount * sizeof ( tItem )) );
  385.     theErr = MemError ( );
  386.     HSetState ( theHan, theState );
  387.     
  388.     return theErr;
  389. }
  390.  
  391.  
  392.  
  393. //
  394. // Appends the data in source at given offset of length to target file.
  395. // The offset of the new data in target is returned in offset.
  396. //
  397. OSErr AppendFileData ( FSSpecPtr source, FSSpecPtr target, long* offset, long* length )
  398. {
  399.     OSErr    theErr;
  400.     short    refNum;
  401.     Ptr        theData;
  402.     long    inOutCount;
  403.     
  404.     
  405.     
  406.     //
  407.     // TO DO:
  408.     //        Don't want to depend on having a large enough block
  409.     //        for the entire fragment. Try our heap, then temporary
  410.     //        memory, and finally resort to doing it bit by bit.
  411.     //
  412.     
  413.     theErr = FSpOpenDF ( source, fsRdPerm, &refNum );
  414.     
  415.     // A length of zero indicates ‘until the EOF’. We'll update
  416.     // the length now, and return it back to the caller.
  417.     if ( *length == 0 )
  418.         theErr = GetEOF ( refNum, length );
  419.     
  420.     theData = NewPtr ( *length );
  421.     if ( theData )
  422.     {
  423.         inOutCount = *length;
  424.         // On entry, ‘offset’ is the offset to the data in source file
  425.         theErr = SetFPos ( refNum, fsFromStart, *offset );
  426.         theErr = FSRead ( refNum, &inOutCount, theData );
  427.         FSClose ( refNum );
  428.         
  429.         FSpOpenDF ( target, fsRdWrPerm, &refNum );
  430.         theErr = SetFPos ( refNum, fsFromLEOF, 0L );
  431.         // On exit, ‘offset’ is the offset to the data in target file
  432.         theErr = GetFPos ( refNum, offset );
  433.         inOutCount = *length;
  434.         theErr = FSWrite ( refNum, &inOutCount, theData );    
  435.         
  436.         // Dispose of the buffer
  437.         DisposePtr ( theData );
  438.     }
  439.     
  440.     // Close the file - ‘source’ if memory alloc failed, ‘target’ if sucessful
  441.     FSClose ( refNum );
  442.     
  443.     
  444.     return noErr;
  445. }
  446.  
  447.  
  448.  
  449. //
  450. //    Deletes the block of data from the data fork at a given offset and length.
  451. //    It achieves this in one of two ways. If the data to be deleted is at the
  452. //    logical EOF, it simply resets the logical EOF to the given offset. If data
  453. //    exists after the data to be deleted, that data is read and writen back at
  454. //    the given offset. 
  455. // 
  456. static OSErr DeleteFileData ( FSSpecPtr theSpec, int32 theOffset, int32 theLength )
  457. {
  458.     OSErr    theErr;
  459.     int16    theRef;
  460.     int32    theEOF;
  461.     int32    inOutCount;
  462.     Ptr        theData;
  463.     
  464.     
  465.     
  466.     theErr = FSpOpenDF ( theSpec, fsRdWrPerm, &theRef );
  467.     if ( theErr )
  468.         return theErr;
  469.     
  470.     theErr = GetEOF ( theRef, &theEOF );
  471.     
  472.     // A length of zero indicates ‘until the EOF’
  473.     if ( theLength == 0 )
  474.         theLength = theEOF - theOffset;
  475.     
  476.     
  477.     // Is the block to be removed at the EOF? For readablility, I'll keep
  478.     // this ‘if’ seperate from the above (length == 0) special condition.
  479.     if ( theEOF == theOffset + theLength )
  480.         theErr = SetEOF ( theRef, theOffset );
  481.     else
  482.     {
  483.         // Data exists after the block to be deleted
  484.         theData = NewPtr ( theEOF - (theOffset + theLength) );
  485.         if ( theData )
  486.         {
  487.             theErr = SetFPos ( theRef, fsFromStart, theOffset + theLength );
  488.             inOutCount = theEOF - (theOffset + theLength);
  489.             theErr = FSRead ( theRef, &inOutCount, theData );
  490.             
  491.             theErr = SetFPos ( theRef, fsFromStart, theOffset );
  492.             inOutCount = theEOF - (theOffset + theLength);
  493.             theErr = FSWrite ( theRef, &inOutCount, theData );
  494.             
  495.             theErr = SetEOF ( theRef, theOffset + inOutCount );
  496.         }
  497.     }
  498.     
  499.     FSClose ( theRef );
  500.     
  501.     return noErr;
  502. }
  503.  
  504.  
  505.  
  506. //
  507. // Each fragment's data stored in a temp file has a single temp
  508. // record. It includes a usage count and the fileSpec. If an item
  509. // doesn't have a record, this routine creates it as it increments
  510. // its usage count.
  511. //
  512. OSErr IncrementTempUsageCount ( tItemPtr theItem )
  513. {
  514.     OSErr    theErr;
  515.     
  516.     if ( theItem->tempFilePtr == nil )
  517.     {
  518.         theItem->tempFilePtr = (tTempFilePtr) NewPtrClear ( sizeof ( tTempFileRec ) );
  519.         theErr = MemError ( );
  520.         if ( theErr )
  521.             return theErr;
  522.         
  523.     }
  524.     
  525.     theItem->tempFilePtr->usageCount++;
  526.     
  527.     return noErr;
  528. }
  529.  
  530.  
  531.  
  532. //
  533. // As each document gets its own copy of the fragment, it decrements the
  534. // usage count. Finally, the temp file is deleted and this storage freed.
  535. //
  536. OSErr DecrementTempUsageCount ( tItemPtr theItem )
  537. {    
  538.     OSErr    theErr;
  539.     
  540.     
  541.     if ( theItem->tempFilePtr )
  542.     {
  543.         theItem->tempFilePtr->usageCount--;
  544.         if ( theItem->tempFilePtr->usageCount == 0 )
  545.         {
  546.             theErr = FSpDelete ( &theItem->tempFilePtr->fileSpec );
  547.             if ( theErr )
  548.                 return theErr;
  549.                 
  550.             DisposePtr ( (Ptr) theItem->tempFilePtr );
  551.             theErr = MemError ( );
  552.             if ( theErr )
  553.                 return theErr;
  554.                 
  555.         }
  556.         theItem->tempFilePtr = nil;
  557.     }
  558.     
  559.     
  560.     return noErr;
  561. }
  562.  
  563.  
  564.  
  565. //
  566. // Find out how many documents are using this temp record
  567. //
  568. int GetTempUsageCount ( tItemPtr theItem )
  569. {
  570.     int theCount = 0;
  571.     
  572.     if ( theItem->tempFilePtr )
  573.         theCount = theItem->tempFilePtr->usageCount;
  574.         
  575.     return theCount;
  576. }
  577.  
  578.  
  579.  
  580. //
  581. // Get the fileSpec for the fragments data
  582. //
  583. FSSpecPtr GetTempSpecPtr ( tItemPtr theItem )
  584. {
  585.     FSSpecPtr    theSpec = nil;
  586.     
  587.     if ( theItem->tempFilePtr )
  588.         theSpec = &theItem->tempFilePtr->fileSpec;
  589.     
  590.     
  591.     #if DEBUGGING
  592.     if ( theSpec == nil )    DebugStr ( "\p GetTempSpecPtr returning nil" );
  593.     #endif
  594.     
  595.     return theSpec;
  596. }
  597.  
  598.  
  599.  
  600.  
  601.  
  602.